package ThirdCommunication;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;


public class ServerThread extends Thread implements BasicData{
    //服务器-客户端 管道
    private Socket client;
    //管道IO流
    private InputStream ips;
    private OutputStream ops;
    private DataInputStream dips;
    private DataOutputStream dops;
    private boolean isInterrupt = false;//用于断开连接时终止线程
    //当前这条线程所连接的用户
    private User user = new User();
    //哈希表，存储所有登记用户的id-pwd
    private static HashMap<String,String> allUsers = new HashMap<>();
    //动态数组，存储所有在运行的ServerThread，等效成存储了所有在线的用户
    private static ArrayList<ServerThread> aliveThreads = new ArrayList<>();
    //动态数组，记录所有在线用户的id，唯一作用是判断是否重复登录
    private static ArrayList<String> aliveIDS = new ArrayList<>();
    //用静态代码块实现类初始化时从属性文件读取数据
    static {
        Properties pro = new Properties();
        try {
            pro.load(new FileReader("D:\\java\\idea\\AllProjects\\ChatRoom\\ServerProject\\UsersInfo\\id-pwd.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        for(String key:pro.stringPropertyNames()){
            allUsers.put(key,pro.getProperty(key));
        }
    }
    //获取用户id-pwd，用于保存
    public HashMap<String,String> getAllUsers(){
        return allUsers;
    }

    public ServerThread(Socket client) throws IOException {
        this.client = client;
        //初始化IO流
        ips = client.getInputStream();
        ops = client.getOutputStream();
        dips = new DataInputStream(ips);
        dops = new DataOutputStream(ops);
    }
    public User getUser(){
        return user;
    }
    //读取客户端发来的信息
    public void readClient() throws IOException {
        //先读取消息总长
        int totallen = dips.readInt();
        //读取消息类型
        byte type = dips.readByte();
        //读取目标对象
        byte[] target = new byte[20];
        dips.readFully(target);//readFully()表示只有读取字节把目标数组填满，才会停止读取
        String targetID = new String(target).trim();//再转成String，切去补长的'\0'，得到目标对象I
        //读取消息体
        byte[] data = new byte[totallen-4-1-20];
        dips.readFully(data);
        //获取完整的消息包后，对type进行判断，执行不同的封装方法
        if(type==registerType){
            respondRegister(data);
        }else if(type==loginType){
            respondLogin(data);
        }else if(type==quitType){
            deleteClient();
        }else if (type==messageType) {
            sendMessage(targetID,data);
        }else if(type==documentType){
            sendFile(targetID,data);
        }
    }
    //向客户端发送信息
    public void writeClient(byte type,String targetID,byte[] data) throws IOException {
        int len = targetLength;//目标对象为定长20的byte[]
        byte[] target = targetID.getBytes();//将targetID转为byte[]
        int totallength = 4+1+20+data.length;//计算总长，不要忘了总长自己占4个字节
        //写入总长
        dops.writeInt(totallength);
        //写入消息类型
        dops.writeByte(type);
        //写入目标对象
        dops.write(target);
        while(len>target.length){//补长
            dops.writeByte('\0');
            len--;
        }
        //写入消息体
        dops.write(data);
    }
    //响应客户端发来的注册请求
    public void respondRegister(byte[] data) throws IOException {
        //注册请求的消息体为长度40的byte[]，前20为id，后20为pwd
        byte[] idByte = new byte[20];
        byte[] pwdByte = new byte[20];
        for(int i=0;i<20;i++){
            idByte[i] = data[i];
        }
        for(int i=0;i<20;i++){
            pwdByte[i] = data[i+20];
        }
        String id = new String(idByte).trim();
        String pwd = new String(pwdByte).trim();
        String result="";//记录注册结果
        //进行检测
        if(!allUsers.containsKey(id)){//该id未注册
            result = "Succeed";
            allUsers.put(id,pwd);
        }else{//该id已经注册
            result = "This id existed.";
        }
        //返回响应结果
        byte type = registerType;//消息类型
        String targetID = id;
        byte[] resultByte = result.getBytes();
        writeClient(type,targetID,resultByte);
    }

    //响应客户端发来的登录请求
    public void respondLogin(byte[] data) throws IOException {
        //登录请求的消息体为长度40的byte[]，前20为id，后20为pwd
        byte[] idByte = new byte[20];
        byte[] pwdByte = new byte[20];
        for(int i=0;i<20;i++){
            idByte[i] = data[i];
        }
        for(int i=0;i<20;i++){
            pwdByte[i] = data[i+20];
        }
        String id = new String(idByte).trim();
        String pwd = new String(pwdByte).trim();
        String result="";//记录登录结果
        //进行检测
        if(allUsers.containsKey(id)){//该id已注册
            if(!allUsers.get(id).equals(pwd)){//pwd错误
                result = "Wrong password.";
            }else if(aliveIDS.contains(id)){//用户已在线
                result = "This id in on line.";
            } else{//登陆成功
                result = "Succeed.";
                //同步当前线程的user对象
                user.setId(id);
                user.setPwd(pwd);
                addClient();
            }
        }else{//该id未注册
            result = "This id does not exist.";
        }
        //返回响应结果
        byte type = loginType;//消息类型
        String targetID = id;
        byte[] resultByte = result.getBytes();
        writeClient(type,targetID,resultByte);
    }
    //向客户端发起添加在线用户的请求
    public void addClient() throws IOException {
        aliveThreads.add(this);//把当前这条新加入的线程加入aliveUsers
        aliveIDS.add(this.getUser().getId());
        //更新好友列表
        for (ServerThread alivethread : aliveThreads) {
            //向新上线的用户发送所有在线用户的id
            writeClient(addUserType,user.getId(),alivethread.getUser().getId().getBytes());
            //向所有已在线的用户发送新上线用户id
            if(alivethread!=this){
                alivethread.writeClient(addUserType,alivethread.getUser().getId(),user.getId().getBytes());
            }
        }
    }
    //响应客户端的退出请求，向其它客户端发起删除在线用户的请求。
    public void deleteClient() throws IOException {
        aliveThreads.remove(this);//把当前这条线程从aliveUsers删除
        aliveIDS.remove(this.getUser().getId());
        //更新好友列表
        for (ServerThread alivethread : aliveThreads) {
            //向所有已在线的用户发送下线用户id
            alivethread.writeClient(deleteUserType,alivethread.getUser().getId(),user.getId().getBytes());
        }
        isInterrupt = true;//最后终止线程
    }
    //响应客户端的消息请求，targetID是已经收到的这条消息是发给谁的；fromID是已经收到的这条消息是谁发的
    public void sendMessage(String targetID,byte[] data) throws IOException {
        //发送这条消息的id的字节数组 forbyte[]
        String fromID = user.getId();
        int len = fromID.getBytes().length;
        for(int i=len;i<20;i++){
            fromID += "\0";
        }
        byte[] frombyte = fromID.getBytes();
        //进行消息体拼接，第0个字节表示群发还是私发，第1到21个字节为发送此消息的用户id
        byte[] resultbyte = new byte[1+20+data.length];
        for(int i=0;i<20;i++){
            resultbyte[1+i] = frombyte[i];
        }
        for(int i=0;i<data.length;i++){
            resultbyte[1+20+i] = data[i];
        }
        if(targetID.equals("0")){//代表这条消息群发
            resultbyte[0] = 0;
            for (ServerThread aliveThread : aliveThreads) {
                aliveThread.writeClient(messageType,aliveThread.getUser().getId(),resultbyte);
            }
        }else{//代表这条消息是私发
            resultbyte[0] = 1;
            //找到要私发的用户对应的线程，发送信息
            for (ServerThread aliveThread : aliveThreads) {
                String idid = aliveThread.getUser().getId();
                if(idid.equals(targetID)){
                    aliveThread.writeClient(messageType,idid,resultbyte);
                    break;
                }
            }
        }
    }
    //响应客户端的发送文件请求
    public void sendFile(String targetID,byte[] data) throws IOException {
        //此处文件是以字节流的形式发送，因此借助发送消息的方法
        //发送这条消息的id的字节数组 forbyte[]
        String fromID = user.getId();
        int len = fromID.getBytes().length;

        //按照消息协议，将发送方ID拼接成20个字节
        for(int i=len;i<20;i++){
            fromID += "\0";
        }
        byte[] frombyte = fromID.getBytes();
        //进行消息体拼接，第0个字节表示群发还是私发，第1到21个字节为发送此消息的用户id
        byte[] resultbyte = new byte[1+20+data.length];
        for(int i=0;i<20;i++){
            resultbyte[1+i] = frombyte[i];
        }
        for(int i=0;i<data.length;i++){
            resultbyte[1+20+i] = data[i];
        }
        if(targetID.equals("0")){//代表这条消息群发
            resultbyte[0] = 0;
            for (ServerThread aliveThread : aliveThreads) {
                if(aliveThread!=this){
                    aliveThread.writeClient(documentType,aliveThread.getUser().getId(),resultbyte);
                }
            }
        }else{//代表这条消息是私发
            resultbyte[0] = 1;
            //找到要私发的用户对应的线程，发送信息
            for (ServerThread aliveThread : aliveThreads) {
                String idid = aliveThread.getUser().getId();
                if(idid.equals(targetID)){
                    aliveThread.writeClient(documentType,idid,resultbyte);
                    break;
                }
            }
        }
    }
    //发送通过特定事件触发，服务器要持续做的是接收
    public void run(){
        while(!isInterrupt){
            try {
                readClient();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}



